/*******************************************************************************
 * @file Copyright 2009 The CREATE Lab at CMU
 * 
 *       This file is part of JubJub, an open source project.
 *       http://code.google.com/p/jubjub/
 * 
 *       Redistribution and use in source and binary forms, with or without
 *       modification, are permitted provided that the following conditions are
 *       met:
 * 
 *       Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 * 
 *       Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 *       Neither the name of Carnegie Mellon nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 * 
 *       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *       HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

package edu.cmu.ri.createlab.jj.visitors;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import edu.cmu.ri.createlab.jj.blocks.*;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.*;
import edu.cmu.ri.createlab.jj.blocks.TaggedCodePiece.TCPType;
import edu.cmu.ri.createlab.jj.projects.*;

public class BVXMLWriter implements BlockVisitorInterface
{
   ///Maintains a list of all blocks belonging to a class
   ///that have been found
   private ArrayList<BlockInterface> classBlocks;
   ///Maintains a list of all blocks that don't go in a class
   ///that have been found
   private ArrayList<BlockInterface> freeBlocks;
   ///The output that's being generated
   ///Only process a single project or library at a time as it
   ///needs to clear everything to start a new one
   private StringBuffer code;
   ///File name that the code will be output to
   private String filename;
   ///How many indents we're currently at
   private int tabCount;
   
   public BVXMLWriter()
   {
      classBlocks = new ArrayList<BlockInterface>();
      freeBlocks = new ArrayList<BlockInterface>();
      code = new StringBuffer(20000);
      filename = null;
   }
   
   ///resets all the data in this visitor
   public void clear()
   {
      classBlocks.clear();
      freeBlocks.clear();
      code.delete(0, code.length());
      filename = null;
   }
   
   
   public BVReturn visit(BlockBlock block)
   {
      //TODO replace all block type names with BlockTypes.TYPE.toString()
      code.append("\n" + getTabs() + "<" + BlockTypes.BLOCK.toString() + ">");
      tabCount++;
      code.append("\n" + getTabs() + "<name>");
      code.append(xmlSafe(block.getName()));
      code.append("</name>");
      code.append("\n" + getTabs() + "<id>");
      int index = classBlocks.indexOf(block);
      if(index != -1) code.append(index);
      else
      {
         index = freeBlocks.indexOf(block);
         if(index == -1) System.err.println("Error:Visited non-existent block!");
         code.append(classBlocks.size() + index);
      }
      code.append("</id>");
      //TODO write prefix, callType, comment, isconstructor, access keywords,
      //params, returns, code

      if(block.getPrefix() != null && !(block.getPrefix().length() == 0))
      {
         code.append("\n" + getTabs() + "<prefix>");
         code.append(xmlSafe(block.getPrefix()));
         code.append("</prefix>");
      }
         
      code.append("\n" + getTabs() + "<callType>");
      code.append(xmlSafe(block.getCallType().toString()));
      code.append("</callType>");
      
      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }
      
      code.append("\n" + getTabs() + "<constructor>");
      code.append(block.isConstructor());
      code.append("</constructor>");
      
      AccessTypes[] tempAcc = block.getAccess();
      if(tempAcc != null)
      {
         for(int i = 0; i < tempAcc.length; i++)
         {
            code.append("\n" + getTabs() + "<access>");
            code.append(xmlSafe(tempAcc[i].toString()));
            code.append("</access>");
         }
      }
      
      code.append("\n" + getTabs() + "<vars>");
      tabCount++;
      writeIO(block.getVars());
      tabCount--;
      code.append("\n" + getTabs() + "</vars>");

      code.append("\n" + getTabs() + "<params>");
      tabCount++;
      writeIO(block.getParams());
      tabCount--;
      code.append("\n" + getTabs() + "</params>");

      code.append("\n" + getTabs() + "<returns>");
      tabCount++;
      writeRefIO(block.getReturns());
      tabCount--;
      code.append("\n" + getTabs() + "</returns>");
      
      code.append("\n" + getTabs() + "<blockCode>");
      tabCount++;
      Iterator<RefBlock> rIt = block.getCode().iterator();
      while(rIt.hasNext())
      {
         rIt.next().accept(this);
      }
      tabCount--;
      code.append("\n" + getTabs() + "</blockCode>");
      tabCount--;
      code.append("\n" + getTabs() + "</" + BlockTypes.BLOCK.toString() + ">");
      return null;
   }

   
   public BVReturn visit(CodeBlock block)
   {
      code.append("\n" + getTabs() + "<CodeBlock>");
      tabCount++;
      code.append("\n" + getTabs() + "<name>");
      code.append(xmlSafe(block.getName()));
      code.append("</name>");
      code.append("\n" + getTabs() + "<id>");
      int index = classBlocks.indexOf(block);
      if(index != -1) code.append(index);
      else
      {
         index = freeBlocks.indexOf(block);
         if(index == -1) System.err.println("Error:Visited non-existent block!");
         code.append(classBlocks.size() + index);
      }
      code.append("</id>");

      if(block.getPrefix() != null && !(block.getPrefix().length() == 0))
      {
         code.append("\n" + getTabs() + "<prefix>");
         if(block.getPrefix() != null) code.append(xmlSafe(block.getPrefix()));
         code.append("</prefix>");
      }

      code.append("\n" + getTabs() + "<callType>");
      code.append(xmlSafe(block.getCallType().toString()));
      code.append("</callType>");
      
      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }
      
      code.append("\n" + getTabs() + "<constructor>");
      code.append(block.isConstructor());
      code.append("</constructor>");
      
      AccessTypes[] tempAcc = block.getAccess();
      if(tempAcc != null)
      {
         for(int i = 0; i < tempAcc.length; i++)
         {
            code.append("\n" + getTabs() + "<access>");
            code.append(xmlSafe(tempAcc[i].toString()));
            code.append("</access>");
         }
      }
      
      code.append("\n" + getTabs() + "<params>");
      tabCount++;
      Iterator<VarBlock> vIt = block.getParams().iterator();
      while(vIt.hasNext())
      {
         vIt.next().accept(this);
      }
      tabCount--;
      code.append("\n" + getTabs() + "</params>");
      code.append("\n" + getTabs() + "<returns>");
      tabCount++;
      vIt = block.getReturns().iterator();
      while(vIt.hasNext())
      {
         vIt.next().accept(this);
      }
      tabCount--;
      code.append("\n" + getTabs() + "</returns>");
      
      code.append("\n" + getTabs() + "<taggedCode>");
      tabCount++;
      writeTaggedCode(block.getTaggedCode());
      tabCount--;
      code.append("\n" + getTabs() + "</taggedCode>");
      tabCount--;
      code.append("\n" + getTabs() + "</CodeBlock>");
      return null;
   }

   
   public BVReturn visit(ExpressionBlock block)
   {
      code.append("\n" + getTabs() + "<ExpressionBlock>");
      tabCount++;
      code.append("\n" + getTabs() + "<id>");
      int index = classBlocks.indexOf(block);
      if(index == -1)
      {
         index = freeBlocks.indexOf(block);
         index += classBlocks.size();
      }
      code.append(index);
      code.append("</id>");
      
      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }
      
      code.append("\n" + getTabs() + "<etype>");
      code.append(xmlSafe(block.getEType().toString()));
      code.append("</etype>");
      
      code.append("\n" + getTabs() + "<symbol>");
      code.append(xmlSafe(block.getSymbol()));
      code.append("</symbol>");
      
      if(block.getFirstIn() != null) block.getFirstIn().accept(this);
      if(block.getSecondIn() != null) block.getSecondIn().accept(this);
      
      tabCount--;
      code.append("\n" + getTabs() + "</ExpressionBlock>");
      return null;
   }

   
   public BVReturn visit(FlowBlock block)
   {
      code.append("\n" + getTabs() + "<FlowBlock>");
      tabCount++;
      code.append("\n" + getTabs() + "<name>");
      code.append(xmlSafe(block.getName()));
      code.append("</name>");
      
      code.append("\n" + getTabs() + "<id>");
      int index = classBlocks.indexOf(block);
      if(index == -1)
         index = classBlocks.size() + freeBlocks.indexOf(block);
      code.append(index);
      code.append("</id>");
      
      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }
      
      code.append("\n" + getTabs() + "<middles>");
      code.append(block.getMiddles());
      code.append("</middles>");
      
      for(int i = 0; i < 4; i++)
      {
         if(!block.getParams(i).isEmpty())
         {
            code.append("\n" + getTabs() + "<params" + i + ">");
            tabCount++;
            writeIO(block.getParams(i));
            tabCount--;
            code.append("\n" + getTabs() + "</params" + i + ">");
         }
      }
      
      for(int i = 0; i < 4; i++)
      {
         if(!block.getCode(i).isEmpty())
         {
            code.append("\n" + getTabs() + "<code" + i + ">");
            tabCount++;
            writeTaggedCode(block.getCode(i));
            tabCount--;
            code.append("\n" + getTabs() + "</code" + i + ">");
         }
      }
      
      tabCount--;
      code.append("</FlowBlock>");
      
      return null;
   }

   
   public BVReturn visit(VarBlock block)
   {
      code.append("\n" + getTabs() + "<VarBlock>");
      tabCount++;
      code.append("\n" + getTabs() + "<name>");
      code.append(xmlSafe(block.getName()));
      code.append("</name>");
      
      code.append("\n" + getTabs() + "<id>");
      int index = classBlocks.indexOf(block);
      if(index == -1)
      {
         index = freeBlocks.indexOf(block);
         if(index == -1)
         {
            freeBlocks.add(block);
            index = freeBlocks.indexOf(block);
         }
         index += classBlocks.size();
      }
      code.append(index);
      code.append("</id>");
      
      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }

      AccessTypes[] tempAcc = block.getAccess();
      if(tempAcc != null)
      {
         for(int i = 0; i < tempAcc.length; i++)
         {
            code.append("\n" + getTabs() + "<access>");
            code.append(xmlSafe(tempAcc[i].toString()));
            code.append("</access>");
         }
      }

      code.append("\n" + getTabs() + "<varType>");
      code.append(xmlSafe(block.getVarType().toString()));
      code.append("</varType>");
      
      code.append("\n" + getTabs() + "<literal>");
      code.append(block.isLiteral());
      code.append("</literal>");
      
      code.append("\n" + getTabs() + "<length>");
      code.append(block.getLength());
      code.append("</length>");
      
      
      for(int i = 0; i < block.getValueLength(); i++)
      {
         if(block.getValue(i) != null)
         {
            code.append("\n" + getTabs() + "<value>");
            code.append(block.getValue(i));
            code.append("</value>");
         }
      }
      
      if(block.getObject() != null)
      {
         int index2 = classBlocks.indexOf(block.getObject());
         if(index2 == -1)
         {
            index2 = freeBlocks.indexOf(block.getObject());
            if(index2 == -1)
            {
               freeBlocks.add(block.getObject());
               index2 = freeBlocks.indexOf(block.getObject());
            }
            index2 += classBlocks.size();
         }
         block.getObject().accept(this);
      }
      
      if(block.getObjectType() != null)
      {
         code.append("\n" + getTabs() + "<objectType>");
         code.append(xmlSafe(block.getObjectType()));
         code.append("</objectType>");
      }
      
      tabCount--;
      code.append("\n" + getTabs() + "</VarBlock>");
      return null;
   }

   
   public BVReturn visit(RefBlock block)
   {
      code.append("\n" + getTabs() + "<ref>");
      tabCount++;
      code.append("\n" + getTabs() + "<name>");
      code.append(xmlSafe(block.getName()));
      code.append("</name>");
      code.append("\n" + getTabs() + "<id>");
      if(block.getBlock() == null || !block.getBlock().isValid())
      {
         code.append("-1");
      }
      else
      {
         int index = classBlocks.indexOf(block.getBlock());
         if(index == -1)
         {
            index = freeBlocks.indexOf(block.getBlock());
            if(index == -1)
            {
               freeBlocks.add(block.getBlock());
               index = freeBlocks.indexOf(block.getBlock());
            }
            code.append(classBlocks.size() + index);
         }
         else code.append(index);
      }
      code.append("</id>");

      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }
      
      if(!block.getParams().isEmpty())
      {
         code.append("\n" + getTabs() + "<params>");
         tabCount++;
         Iterator<RefBlock> rIt = block.getParams().iterator();
         while(rIt.hasNext())
         {
            rIt.next().accept(this);
         }
         tabCount--;
         code.append("\n" + getTabs() + "</params>");
      }
      if(!block.getReturns().isEmpty())
      {
         code.append("\n" + getTabs() + "<returns>");
         tabCount++;
         Iterator<RefBlock> rIt = block.getReturns().iterator();
         while(rIt.hasNext())
         {
            rIt.next().accept(this);
         }
         tabCount--;
         code.append("\n" + getTabs() + "</returns>");
      }
      tabCount--;
      code.append("\n" + getTabs() + "</ref>");
      return null;
   }

   
   public BVTypes getBVType()
   {
      return BVTypes.XML;
   }

   
   public BVReturn visit(ObjectBlock block)
   {
      code.append("\n" + getTabs() + "<ObjectBlock>");
      tabCount++;
      code.append("\n" + getTabs() + "<name>");
      code.append(xmlSafe(block.getName()));
      code.append("</name>");
      
      code.append("\n" + getTabs() + "<id>");
      int index = classBlocks.indexOf(block);
      if(index == -1)
         index = classBlocks.size() + freeBlocks.indexOf(block);
      code.append(index);
      code.append("</id>");
      
      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }
      
      if(block.getJJClass() != null)
      {
         code.append("\n" + getTabs() + "<cName>");
         code.append(xmlSafe(block.getJJClass().getName()));
         code.append("</cName>");
      }
      
      if(block.getObjectType() != null && !(block.getObjectType().length() == 0))
      {
         code.append("\n" + getTabs() + "<objectType>");
         code.append(xmlSafe(block.getObjectType()));
         code.append("</objectType>");
      }
         
      code.append("\n" + getTabs() + "<length>");
      code.append(block.getLength());
      code.append("</length>");
      
      code.append("\n" + getTabs() + "<variable>");
      index = classBlocks.indexOf(block.getVariable());
      if(index == -1)
         index = classBlocks.size() + freeBlocks.indexOf(block.getVariable());
      code.append(index);
      code.append("</variable>");
      
      tabCount--;
      code.append("\n" + getTabs() + "</ObjectBlock>");
      return null;
   }

   
   public BVReturn visit(CommentBlock block)
   {
      code.append("\n" + getTabs() + "<CommentBlock>");
      tabCount++;
      
      code.append("\n" + getTabs() + "<name>");
      code.append(xmlSafe(block.getName()));
      code.append("</name>");
      
      code.append("\n" + getTabs() + "<id>");
      int index = classBlocks.indexOf(block);
      if(index == -1)
         index = classBlocks.size() + freeBlocks.indexOf(block);
      code.append(index);
      code.append("</id>");
      
      if(block.getComment() != null && !(block.getComment().length() == 0))
      {
         code.append("\n" + getTabs() + "<comment>");
         code.append(xmlSafe(block.getComment()));
         code.append("</comment>");
      }
      
      tabCount--;
      code.append("\n" + getTabs() + "</CommentBlock>");
      return null;
   }

   
   public BVReturn visit(JJClass jjclass)
   {
      // TODO Auto-generated method stub
      code.append("\n\t<class>");
      code.append("\n\t\t<name>");
      code.append(xmlSafe(jjclass.getName()));
      code.append("</name>");
      if(jjclass.getComment() != null && !(jjclass.getComment().length() == 0))
      {
         code.append("\n\t\t<comment>");
         code.append(xmlSafe(jjclass.getComment()));
         code.append("</comment>");
      }
      tabCount = 2;
      Iterator<VarBlock> vIt = jjclass.getVars().iterator();
      while(vIt.hasNext())
      {
         vIt.next().accept(this);
      }
      Iterator<BlockInterface> bIt = jjclass.getConstructors().iterator();
      while(bIt.hasNext())
      {
         bIt.next().accept(this);
      }
      bIt = jjclass.getInlineBlocks().iterator();
      while(bIt.hasNext())
      {
         bIt.next().accept(this);
      }
      bIt = jjclass.getMethodBlocks().iterator();
      while(bIt.hasNext())
      {
         bIt.next().accept(this);
      }
      code.append("\n\t</class>");
      return null;
   }

   
   public BVReturn finish()
   {
      if(filename == null || filename.length() < 4)
      {
         System.err.println("Error:Cannot write class without a name.");
         return null;
      }
      try
      {
         BufferedWriter out = new BufferedWriter(new FileWriter(filename));
         out.write(code.toString());
         out.close();
      } catch(IOException e)
      {
         System.err.println(e.getMessage());
      }
         
      return null;
   }

   
   public BVReturn visit(JJLibrary jjlib)
   {
    ///clear the writer
      this.clear();
      ///set the filename
      filename = Block.toCallName(jjlib.getName()) + ".jjl";
      ///add the project name and date to a new project tab
      code.append("<library>");
      code.append("\n\t<lname>");
      code.append(xmlSafe(jjlib.getName()));
      code.append("</lname>");
      code.append("\n\t<date>");
      code.append(xmlSafe(new Date().toString()));
      code.append("</date>");
      
      ///First pass just store links to all the class blocks
      ///This is to make numbering them for reference easy
      Iterator<JJClass> cIt = jjlib.getClasses().iterator();
      while(cIt.hasNext())
      {
         JJClass tempC = cIt.next();
         Iterator<VarBlock> vIt = tempC.getVars().iterator();
         while(vIt.hasNext())
         {
            this.classBlocks.add(vIt.next());
         }
         Iterator<BlockInterface> bIt = tempC.getConstructors().iterator();
         while(bIt.hasNext())
         {
            this.classBlocks.add(bIt.next());
         }
         bIt = tempC.getInlineBlocks().iterator();
         while(bIt.hasNext())
         {
            this.classBlocks.add(bIt.next());
         }
         bIt = tempC.getMethodBlocks().iterator();
         while(bIt.hasNext())
         {
            this.classBlocks.add(bIt.next());
         }
      }
      ///Second pass we actually start writing the output
      cIt = jjlib.getClasses().iterator();
      while(cIt.hasNext())
      {
         cIt.next().accept(this);
      }
      ///Now we output any blocks that don't belong in a class
      code.append("\n\t<freeblocks>");
      
      for(int i = 0; i < freeBlocks.size(); i++)
      {
         ///Object blocks are written in the variable that calls them.
         ///They're only in the freeBlocks list for id numbering.
         if(freeBlocks.get(i).getType() != BlockTypes.OBJECT) 
            freeBlocks.get(i).accept(this);
      }
      
      code.append("\n\t</freeblocks>");
      code.append("\n</library>");
      return null;
   }

   
   public BVReturn visit(JJProject jjproj)
   {      
      ///clear the writer
      this.clear();
      ///set the filename
      filename = Block.toCallName(jjproj.getName()) + ".jj";
      ///Define the xml version
      code.append("<?xml version=\"1.0\"?>\n");
      ///add the project name and date to a new project tab
      code.append("<project>");
      code.append("\n\t<pname>");
      code.append(xmlSafe(jjproj.getName()));
      code.append("</pname>");
      code.append("\n\t<date>");
      code.append(xmlSafe(new Date().toString()));
      code.append("</date>");
      
      ///List any required libraries
      Iterator<JJLibrary> lIt = jjproj.getLibraries().iterator();
      while(lIt.hasNext())
      {
         code.append("\n\t<library>");
         code.append(xmlSafe(lIt.next().getName()));
         code.append("</library>");
      }
      ///First pass just store links to all the class blocks and VarBlocks
      ///This is to make numbering them for reference easy
      Iterator<JJClass> cIt = jjproj.getClasses().iterator();
      while(cIt.hasNext())
      {
         JJClass tempC = cIt.next();
         Iterator<VarBlock> vIt = tempC.getVars().iterator();
         while(vIt.hasNext())
         {
            this.classBlocks.add(vIt.next());
         }
         Iterator<BlockInterface> bIt = tempC.getConstructors().iterator();
         while(bIt.hasNext())
         {
            BlockInterface tempBlock = bIt.next();
            this.classBlocks.add(tempBlock);
            addVars(tempBlock);
         }
         bIt = tempC.getInlineBlocks().iterator();
         while(bIt.hasNext())
         {
            BlockInterface tempBlock = bIt.next();
            this.classBlocks.add(tempBlock);
            addVars(tempBlock);
         }
         bIt = tempC.getMethodBlocks().iterator();
         while(bIt.hasNext())
         {
            BlockInterface tempBlock = bIt.next();
            this.classBlocks.add(tempBlock);
            addVars(tempBlock);
         }
      }
      ///Second pass we actually start writing the output
      cIt = jjproj.getClasses().iterator();
      while(cIt.hasNext())
      {
         cIt.next().accept(this);
      }
      ///Outputs the tabs
      Iterator<JJTab> tIt = jjproj.getTabs().iterator();
      while(tIt.hasNext())
      {
         code.append("\n\t<tab>");
         
         JJTab tempTab = tIt.next();
         code.append("\n" + getTabs() + "<name>");
         code.append(xmlSafe(tempTab.getName()));
         code.append("</name>");
         
         Iterator<JJSubTab> sIt = tempTab.getSubTabs().iterator();
         while(sIt.hasNext())
         {
            JJSubTab tempST = sIt.next();
            code.append("\n" + getTabs() + "<subtab>");
            tabCount++;
            code.append("\n" + getTabs() + "<name>");
            code.append(xmlSafe(tempST.getName()));
            code.append("</name>");
            tempST.accept(this);
            tabCount--;
            code.append("\n" + getTabs() + "</subtab>");
         }
         code.append("\n\t</tab>");
      }
      ///Now we output any blocks that don't belong in a class
      code.append("\n\t<freeblocks>");
      
      for(int i = 0; i < freeBlocks.size(); i++)
      {
         ///Object blocks are written in the variable that calls them.
         ///They're only in the freeBlocks list for id numbering.
         if(freeBlocks.get(i).getType() != BlockTypes.OBJECT) 
            freeBlocks.get(i).accept(this);
      }
      
      code.append("\n\t</freeblocks>");
      code.append("\n</project>");
      return null;
   }
   
   ///Adds all the vars in a CodeBlock or BlockBlock to the classBlocks list
   private void addVars(BlockInterface tempBlock)
   {
      Iterator<VarBlock> vIt = new ArrayList<VarBlock>().iterator();
      Iterator<RefBlock> rIt = new ArrayList<RefBlock>().iterator();
      if(tempBlock.getType() == BlockTypes.BLOCK)
      {
         vIt = ((BlockBlock)tempBlock).getVars().iterator();
         while(vIt.hasNext()) this.classBlocks.add(vIt.next());
         vIt = ((BlockBlock)tempBlock).getParams().iterator();
      }
      else if(tempBlock.getType() == BlockTypes.CODE)
         vIt = ((CodeBlock)tempBlock).getParams().iterator();
      while(vIt.hasNext()) this.classBlocks.add(vIt.next());
      
      if(tempBlock.getType() == BlockTypes.BLOCK)
         rIt = ((BlockBlock)tempBlock).getReturns().iterator();
      else if(tempBlock.getType() == BlockTypes.CODE)
         vIt = ((CodeBlock)tempBlock).getReturns().iterator();
      while(vIt.hasNext()) this.classBlocks.add(vIt.next());
   }
   
   ///Goes through a params or returns list and visits each reference
   private void writeIO(List<VarBlock> ioList)
   {
      Iterator<VarBlock> vIt = ioList.iterator();
      while(vIt.hasNext())
      {
         vIt.next().accept(this);
      }
   }
   
   ///Goes through a refblock returns list and visits each reference
   private void writeRefIO(List<RefBlock> ioList)
   {
      Iterator<RefBlock> rIt = ioList.iterator();
      while(rIt.hasNext())
         rIt.next().accept(this);
   }
   
   ///Goes through a tagged code list and writes out the contents to the code buffer
   private void writeTaggedCode(List<TaggedCodePiece> tc)
   {
      if(tc != null && !tc.isEmpty())
      {
         Iterator<TaggedCodePiece> tIt = tc.iterator();
         while(tIt.hasNext())
         {
            TaggedCodePiece tcp = tIt.next();
            if(tcp.getType() == TCPType.CODE)
            {
               code.append("\n" + getTabs() + "<code>");
               code.append(xmlSafe(tcp.getContent()));
               code.append("</code>");
            }
            else
            {
               code.append("\n" + getTabs() + "<var>");
               code.append(xmlSafe(tcp.getContent()));
               code.append("</var>");
            }
         }
      }
   }
   
   ///Replaces '&', '<', and '>' in the given string to make it XML friendly
   private String xmlSafe(String s)
   {
      String xs = s;
      xs = xs.replaceAll("&", "&amp;");
      xs = xs.replaceAll("<", "&lt;");
      xs = xs.replaceAll(">", "&gt;");
      return xs;
   }
   
   ///returns a string of tabCount tabs
   private String getTabs()
   {
      String s = "";
      for(int i = 0; i < tabCount; i++)
      {
         s += "\t";
      }
      return s;
   }

   ///Test method for the writer
   public static void main(String[] args)
   {
      JJProject helloWorldProj;
      
      BVXMLWriter xWrite = new BVXMLWriter();
      
//      
//      try
//      {
         helloWorldProj = JJProject.getTestProject();
               
         helloWorldProj.accept(xWrite);
         
         xWrite.finish();
         
         
//      } catch(Exception e)
//      {
//         System.err.println(e.getMessage());
//      }
      
   }
}


